/**
 * @class {Root} 场景管理器
 * @author DuskyHuang 圣子
*/

import { director } from 'cc';
import { __cfw_notifications__ } from '../configs';
import { inner, managers } from '../core/boot';
import { LoadingPage } from '../ui/loading-page';
import { singleton } from '../utils/decorators';
import UIManager from './ui-manager';

@singleton
export default class SceneManager {

    declare public static readonly instance: SceneManager;
    public useLoadingPage: boolean = true;
    public homeName: string = null;
    public hasLoaded(scene: string): boolean { return this._loaded.contains(scene); }
    public addLoaded(scene: string): this { this._loaded.pushUnique(scene); return this; }
    public pushRecord(scene: string): this { this._record.push(scene); return this; }
    public clearChain(): this { this._chain.length = 0; return this; }
    public addChain(...scenes: string[]): this { this._chain.pushUnique(...scenes); return this; }
    public get thisName(): string { return director.getScene().name; }
    public get nextInChain(): string | null { return this._chain[this._chain.indexOf(this.thisName) + 1] ?? null; }
    public get lastInChain(): string | null { return this._chain[this._chain.indexOf(this.thisName) - 1] ?? null; }
    private _chain: string[] = [];
    private _loaded: string[] = [];
    private _record: string[] = [];
    private get _loadingPage(): LoadingPage { return UIManager.instance.loadingPage; }

    private _loadScene(scene: string): void {
        const { useLoadingPage, _loadingPage: page } = this;
        if (useLoadingPage && !!page) {
            page.loadEnter(scene);
        } else {
            director.loadScene(scene, () => this._record.push(scene));
        }
    }

    public async loadEnter(...scenes: string[]): Promise<boolean> {
        if (!notEmptyArr(scenes)) return true;
        const { useLoadingPage, _loadingPage: page } = this;
        if (useLoadingPage && !!page) {
            return await page.loadEnter(...scenes);
        } else {
            let ok = 0;
            return new Promise(resolve => {
                for (let scene of scenes) {
                    director.preloadScene(scene, () => {
                        this._record.push(scene);
                        if (++ok >= scenes.length) {
                            director.loadScene(scenes.head);
                            resolve(true);
                        }
                    });
                }
            });
        }
    }

    public async loadReady(...scenes: string[]): Promise<boolean> {
        if (!notEmptyArr(scenes)) return true;
        const { useLoadingPage, _loadingPage: page } = this;
        if (useLoadingPage && !!page) {
            return await page.loadReady(...scenes);
        } else {
            let ok = 0;
            return new Promise(resolve => {
                for (let scene of scenes) {
                    director.preloadScene(scene, () => {
                        this._record.push(scene);
                        if (++ok >= scenes.length) resolve(true);
                    });
                }
            });
        }
    }

    public preload(...scenes: string[]): void {
        for (let scene of scenes) {
            director.preloadScene(scene, () => {
                this._loaded.pushUnique(scene);
            });
        }
    }

    public goto(scene: string): void {
        this._loadScene(scene);
    }

    public back(): void {
        const name = this._record.pop();
        if (name) this._loadScene(name);
    }

    public head(): void {
        const name = this._chain.head;
        if (name) this._loadScene(name);
    }

    public next(): void {
        const name = this.nextInChain;
        if (name) this._loadScene(name);
    }

    public last(): void {
        const name = this.lastInChain;
        if (name) this._loadScene(name);
    }

    public home(): void {
        const name = this.homeName;
        if (name) this._loadScene(name);
    }
}

managers.scene = SceneManager.instance;